CSS

全面解析IFC、FFC 和 GFC原理

Posted on 2020-05-18,15 min read

IFC前置知识

在讲IFC之前,我们首先要理清楚一些概念,我们才能用专业术语来描述IFC的特性,下面我们一一来讲解:

行高四线

我们经常听见很多术语,比如文字对齐的基线中线等,但是似乎我们一直都没怎么关注它们具体的位置在哪,只是大概知道基线在文字的下面,中线在文字的中间,现在我们就来真正的弄懂这些线在哪,行高四线顶线中线基线底线,它们的具体位置如下:

  • 顶线:父元素 font-size 大小所组成的一个内容区域的顶部
  • 中线:小写英文字母x的交叉线的中间
  • 基线:小写英文字母x的下端沿
  • 底线:父元素 font-size 大小所组成的一个内容区域的底部

中线基线我们都有明确的位置指向,分别是小写字母x中间相交那个点,而基线就是小写字母x底端沿的那条线,下面我们来用一张图作解释说明:

我们可以看到基线就是小写字母x底端沿,那么顶线和底线的具体位置在那呢,我们来看一个例子:

我们针对文字设置不同的字体大小,我们可以看到背景的深色的大小就会不一样,这个深色的区域的顶部和底部就是我们所说的顶线底线了,用一张图来说明如下:

行距、行高、半行间距、内容区、行内框和行框

在我们理解行高四线的基础上,我们再来谈谈接下来的6个概念,他们定义分别如下:

  • 行距:相邻两行底线顶线的垂直距离
  • 行高:文本行的基线间的距离
  • 内容区:底线和顶线包裹的区域,由font-size决定
  • 半行间距:(line-height值 – font-size值) / 2
  • 行内框:内容区 + 行间距 即为行内框的大小
  • 行框(line box):虚拟的矩形框,其高度等于本行内所有元素中行内框最大的值

行距,正如其定义的概念一样,是相邻两行之间,上一行的底线和下一行的顶线的垂直距离;而行高则是文本行之间的基线间的距离,可能第一次听到这种说法很诧异,但是在《CSS权威指南》中的却有着明确的记载:

line-height属性是指文本行基线间的距离,而不是字体的大小,它确定了将各个元素框的高度增加或减少多少。

下面来用一张图解释说明,方便更好的理解:

行间距,即内容区行框之间总距离,我们可以通过(line-height值 – font-size值) 算出行间距,举个例子:

<p style="font-size:18px;line-height:24px">demo</p>

通过公式我们可以计算出行间距 24 - 18 = 6 为6,这里有一点需要注意,行间距不一定肯定是正数,当行高比文字大小还小的时候,我们就会得到一个负的行间距。

半行间距顾名思义就是行间距除以一半,所以半行间距的计算公式就是:

(line-height值 - font-size值) / 2

下面我们来看一下具体的半行间距的位置,如下图所示:

我们可以看到半行间距就是斜线阴影区域,分别均匀的分布在内容区的上下两部分,从而组成了一个行内框,在这之中最高的行内框则会形成一个行框,具体概念下面会讲。

行内框,一个抽象的概念,文本行中每一个元素都会生成一个内容区,而每一个内容区都会生成一个行内框,如果排除其它因素的话,这个行内框可以完全等于该元素的内容区,但实际情况几乎都会有其它因素;行内框的大小几乎等同于内容区 + 行间距

行框即一个虚拟的矩形框,它的高度恰好足以包含最高行内框的顶端和最低行内框的底端,换句话说,就是行内框中最高那个即是行框

从上图中我们可以看出简单的文字中间放置了一个图片,文字生成了一个匿名的行内框,而图片默认则是内联元素,也会生成一个行内框,在这里面有三个行内框,其中最高的就是图片所生成的行内框,因此这个行内框则形成了行框,至此,我们大致理清了行框行内框的一些区别。

置换元素

置换元素简而言之就是可替换的元素,它的渲染与css无关,这一类元素比如<img>,<video>等标签,具体说明可查看这里

因为置换元素渲染规则不同于非置换元素,所以这里特别说明一下,具体表现如下:

  • 内容区:height+padding+margin+border的总和
  • 行内框:高度和内容区相等

内容区和行内框的高度非常好理解,就是自身的高度、内边距、外边距以及边框的总和,但是**置换元素没有行间距,自然也没有半行间距,这是需要强调的一个点,另一个点是非置换行内元素**的内边距、外边距和边框对行内框以及元素所在的行框的高度没有影响。

IFC到底是什么

标准是这么定义的:

An inline formatting context is established by a block container box that contains no block-level boxes.
一个IFC由不包含块级盒子块容器盒子建立。

这里面的出现了两个名词块级盒子块容器盒子,由于涉及的知识点太多,这里就不再论述了,想了解可点击这里

定义似乎很简单,但是其渲染的规则却是比BFC可复杂多了,下面我们来一一讲解:

特性一:垂直方向由 vertical-align 控制

每一个盒子自左向右水平排列,盒子相互之间的paddingborder以及margin都会占据对应的空间。在同一水平的垂直方向上,这些盒子可以以不同的方式垂直对齐(vertical-align属性影响)。

这里的每一个盒子都会生成一个行内框行内框的宽高都会不尽相同,最终这一行将会以最大的行内框的大小生成一个行框,从而造成较小的行内框与这个行框在垂直方向产生多余空间,而这个多余空间的具体表现行为则由vertical-align属性控制,下面我们来看一个例子:

这里共有3个行内框,第一个“几个”是匿名行内框,第二个是em所包含的行内框,第三个则是“文本”所在的匿名行内框,这里三个行内框将会以最大的一个作为整个行框大小。

通过设置不同vertical-align属性值我们可以看到em标签所包含“测试”文字会向上或向下偏移,这便是IFC的第一个特性。下面我们总结一下vertical-align属性值具体的位置:

    vertical-align: baseline; //默认效果,强制元素基线与父元素基线对齐
    vertical-align: sub; // 下沉效果,规范未明确,用户代理自行处理
    vertical-align: super; // 上标效果,规范未明确,用户代理自行处理
    vertical-align: text-top; //元素所在`行内框`的顶边与默认文本框顶边对齐
    vertical-align: text-bottom; //元素所在`行内框`的底边与默认文本底顶边对齐
    vertical-align: middle; // //中线对齐,即`x`的交叉线处
    vertical-align: top; // 元素所在`行内框`的顶边和`行框`顶边对齐
    vertical-align: bottom; // 元素所在`行内框`的底边和`行框`底边对齐

特性二:水平方向由text-align控制

当一行中所有的盒子的宽度不足以大于所在行的行框宽度时,则这一行框中的盒子的水平排版由text-align属性决定;一旦这一行中所有的盒子都不足以容纳本行的行框中时,则会发生换行,此时所有的盒子将分布在多个的垂直堆叠的行框之间。

在某些特定语言或属性影响下是无法换行的,比如受white-space:nowrap影响 便会发生行框溢出的效果。在发生拆分的位置其paddingborder以及margin都没有任何视觉效果,没怎么理解?下面来看一个例子:

当我们点击触发换行操作时,em标签周围的虚线红点边框被拆分成了两段,我们仔细看“几个测试”文字的右边缘没有闭合的红色虚线小点,而第三行的开始也没有闭合的红色虚线小点,从这里我们可以得出在发生拆分的位置,其paddingborder以及margin都是没有任何视觉效果的。

特性三:内外边距以及边框对垂直方向上不构成影响

初学CSS时候,可能我们会经常使用padding调整段落文本间的距离,有时候好像很有用,但是有时候好像不怎么成功,下面我们来看一个例子:

我们在输入框中随意修改数值,可以发现红色字体所在的元素似乎对上一行的元素的间隔没有任何影响,当我点击按钮设置其背景色时,我们再去动态修改其上内边距,我们可以发现其背景色会变高变窄,且会覆盖上一行文字,这是为什么呢?因为在默认常规流中,文字都是从上到下,从左到右排列,所以下面的层级便会比上一级的层级要高,所以便会出现下面的背景会遮住上面的文字的视觉效果。同样,无论我们设置红色字体的margin亦或是border都不会撑开垂直方向的高度,虽然在默认情况下我们感觉没有任何变化,但是一旦设置了背景等属性,其垂直方向的还是有一定的影响的。

其它特性我们大多数初学css时便能直观的感受到,包括行内框会在水平方向一个接着一个排列,不足时便会换行;每一行都会生成一个行框,行框中包括这一行的所有的行内框;行框的高度由这一行中最大的行内框计算得出等。

前面的我们讲的都是IFC,如果IFC中混入了一个块级元素,那么结果将会如何?根据前面的论述,我们知道块级元素肯定会生成BFC,那么块级元素两边的行内元素便会生成两个IFC,即使其两边的都是文本,那也会生成两个匿名行内框。

FFC到底是什么

一个设有display:flexdisplay:inline-flex的元素是一个伸缩容器,会为其子元素生成弹性格式化上下文。

定义看起来相当的简洁明了,但是flex布局却是为了css的布局注入新的血液,下面我们来看看FFC有哪些特性,这里暂且跳过与flex相关的属性介绍。

特性一:浮动不会闯入伸缩容器,且不会发生margin合并

在FFC中,对伸缩项目(即所在的父级元素设置了display:flexdisplay:inline-flex)设置浮动是没有任何作用的。(但是特殊情况下还是会有影响的,尽管这是在执行渲染FFC之前就已经确定了,比如值为display:inline的元素在设置浮动后会被强制渲染为display:block),下面来看一个例子:

在红线所在的标签设置了样式display: flex,从而生成了FFC,其中的两个item成为了伸缩项目,每个item我们都设置了margin: 10px,我们可以看到两个item中间间距似乎是item左边的间距的两倍,如果我们点击取消设置display:flex,我们会清晰的发现下面的item会上移一部分,这一部分就是被合并的边距;下面我们继续点击按钮,将**设置display:flex**还原回来,我们点击设置float:right,发现第一个item的第二个普通item往右下角移动了一小截,是否没怎么明白为什么,我们看一下这一段的HTML:

  <div class="flex">
      <div class="item">
          伸缩项目item
          <div class="child">普通child</div>
          <div class="child right">普通child</div>
      </div>
      <div class="item right">伸缩项目item</div>
  </div>

class里面带有right类名将会设置右浮动,而child所在的元素由于不处于FFC中,所以受到浮动的影响右移了一小截,在类名为item的伸缩项目由于是在FFC中,所以浮动对它没有任何影响。最后再说一小点,浮动都不适用了,那么清除浮动的clear当然也不适用了。

特性二:vertical-align在FFC中不适用

以前控制垂直对齐的时候我们都会使用vertical-align属性,但是在FFC中由于是一个全新的分配空间的规则,因此此属性也就不适用,而控制垂直方向上则交给了FFC里面的align-items等属性控制了。

补充说明

补充说明是为了论述FFC原理的完整性,您可以选择跳过本小结。

如果在FFC中设置绝对定位会怎么样,根据规范里的论述,伸缩容器里的绝对定位子元素不参与FFC,但是在一些特殊情景之下还是会受影响,具体可查看规范里面的论述;堆叠上下文(即z-index属性)在FFC中与在常规BFC表现并无差异,不了解堆叠上下文可查看张鑫旭的博文,讲的非常清晰;多栏(column)模块也不适用与FFC,尽管多栏模块兼容性还不怎么好。

GFC到底是什么

一个设有display:grid或display:inline-grid的元素是一个栅格容器,会为其子元素生成栅格格式化上下文。

定义似乎和FFC十分类似,并且很多特性都几乎和FFC有着异曲同工之妙,下面我们来揭开其神秘的面纱:

特性一:浮动不会闯入栅格容器,且不会发生margin合并

这在前面的FFC中特性我们也举例论述过,其渲染效果大同小异,这里就不再详细叙述了。

特性二:vertical-align在GFC中不适用

渲染效果与FFC的第二个特性如出一辙,这里不再讨论。

补充说明

  • column-*(column-count、columns等)多栏模块在GFC将被忽略
  • ::first-line和::first-letter伪元素将被忽略

结语

本文章旨在探讨常用*FC的一些触发生成的原理,以及其渲染的常见的一些特性,目的是为了后续开发过程中能够知晓为何有些属性或渲染似乎和自己理解的有出入,从而加深对css这一块的理解。

本文的概念也是结合笔者自身理解以及查阅了一些文档总结而成,如文中有论述错误的地方,还望批评指正。

参考
[1]img-inline-layout
[2]inline-non-replaced
[3]Css3-flexbox
[4]css权威指南(第四版-第13章栅格布局)

作者:落叶卢生
链接:https://luoyelusheng.com/post/全面解析IFC、FFC 和 GFC原理
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下一篇: 块格式化上下文(Block Formatting Context,BFC) 前世今生→